using System;
using System.Collections.Generic;
using System.Text;
using System.Data.SqlClient;
using System.Threading;

namespace BookBarter
{
    //Central class than defines book objects
    public class Book
    {
        //class Variables
        private static List<Book> books =  new List<Book>();
        private Guid id;
        private String author;
        private String description;
        private String isbn;
        private String language;
        private int numPages;
        private String publisher;
        private String title;
        private String imageUrl;

        //class properties
        public String ImageUrl
        {
            get {return imageUrl;}
            set
            {
                imageUrl = value;
                Save();
            }
        }
        public Guid ID
        {
            get { return id; }
            private set { id = value; }
        }
        public String Author
        {
            get { return author; }
            private set { author = value; }
        }
        public String Description
        {
            get { return description; }
            private set { description = value; }
        }
        public String ISBN
        {
            get { return isbn; }
            private set { isbn = value; }
        }
        public String Language
        {
            get { return language; }
            private set { language = value; }
        }
        public int NumPages
        {
            get { return numPages; }
            private set { numPages = value; }
        }
        public String Publisher
        {
            get { return publisher; }
            private set { publisher = value; }
        }
        public String Title
        {
            get { return title; }
            private set { title = value; }
        }

        //Constructor for the book class
        private Book(Guid bookid, String title, String author, String publisher,
            String language, String isbn, String description, int numPages, String imgURL)
        {
            this.ID = bookid;
            this.Title = title;
            this.Author = author;
            this.Publisher = publisher;
            this.Language = language;
            this.ISBN = isbn;
            this.Description = description;
            this.NumPages = numPages;
            this.ImageUrl = imgURL;
        }

        
        /* Come back to this */
        /// <summary>
        /// Finds all BookHaves for this book.
        /// </summary>
        /// <returns>a list of all BookHaves</returns>
        public List<BookHave> FindAllHaves()
        {
            List<BookHave> allHolds = new List<BookHave>();
            SqlConnection cnx = DatabaseConnectionManager.Current.Connection;
            SqlCommand query = new SqlCommand("EXEC get_all_book_haves", cnx);
            query.Parameters.Add("@bookid", System.Data.SqlDbType.UniqueIdentifier);
            query.Parameters["@bookid"].SqlValue = this.ID;
            SqlDataReader result = query.ExecuteReader();

            try
            {
                while(result.Read())
                 allHolds.Add(BookHave.GetBookHave(result.GetGuid(result.GetOrdinal("haveid"))));
            }
            finally
            {
                result.Close();
            }

            return allHolds;
        }

        /// <summary>
        /// Creates and returns a new book with the given parameters.  
        /// Adds new book to static list of all books
        /// </summary>
        /// <param name="title">book title</param>
        /// <param name="author"> book author</param>
        /// <param name="publisher">book publisher</param>
        /// <param name="language">book language</param>
        /// <param name="isbn">isbn number of the book</param>
        /// <param name="description">book description</param>
        /// <param name="numPages">number of pages in the book</param>
        /// <param name="imageUrl">URL pointing to an image of the book's cover</param>
        /// <returns>Returns the newely created book</returns>
        public static Book CreateBook(String title, String author, String publisher,
            String language, String isbn, String description, int numPages, String imageUrl)
        {
            Book newbook = null;
            SqlConnection cnx = DatabaseConnectionManager.Current.Connection;
            SqlCommand query = new SqlCommand("EXEC add_book @title, @author, " +
            "@publisher, @lang, @isbn, @description, @num_pages, @image_url", cnx);

            query.Parameters.Add("@title", System.Data.SqlDbType.VarChar);
            query.Parameters["@title"].SqlValue = title;
            query.Parameters.Add("@author", System.Data.SqlDbType.VarChar);
            query.Parameters["@author"].SqlValue = author;
            query.Parameters.Add("@publisher", System.Data.SqlDbType.VarChar);
            query.Parameters["@publisher"].SqlValue = publisher;
            query.Parameters.Add("@lang", System.Data.SqlDbType.VarChar);
            query.Parameters["@lang"].SqlValue = language;
            query.Parameters.Add("@isbn", System.Data.SqlDbType.VarChar);
            query.Parameters["@isbn"].SqlValue = isbn;
            query.Parameters.Add("@description", System.Data.SqlDbType.Text);
            query.Parameters["@description"].SqlValue = description;
            query.Parameters.Add("@num_pages", System.Data.SqlDbType.Int);
            query.Parameters["@num_pages"].SqlValue = numPages;
            query.Parameters.Add("@image_url", System.Data.SqlDbType.VarChar);
            query.Parameters["@image_url"].SqlValue = imageUrl;
            SqlDataReader result = query.ExecuteReader();
            try
            {
                if (result.HasRows)
                {
                    if (result.Read())
                    {
                        if (result.IsDBNull(result.GetOrdinal("bookid")))
                        {
                            result.Close();
                            throw new MalformedBookRecord("NULL where data is expected for record ID.");
                        }
                        newbook = Book.GetBook(result.GetGuid(result.GetOrdinal("bookid")));
                    }
                    else
                        throw new NewBookException("No book exists for the new book id.");
                }
            }
            finally
            {
                result.Close();
            }
            books.Add(newbook);
            return newbook;
        }

        /// <summary>
        /// Saves the current book to the database.
        /// </summary>
        private void Save()
        {
            //grab connection to dbms
            SqlConnection cnx = DatabaseConnectionManager.Current.Connection;
            SqlCommand query = new SqlCommand("EXEC update_book @bookid, @title, @author, " +
                "@publisher, @lang, @isbn, @description, @num_pages, @image_url", cnx);
            query.Parameters.Add("@bookid", System.Data.SqlDbType.UniqueIdentifier);
            query.Parameters["@bookid"].SqlValue = this.ID;

            query.Parameters.Add("@title", System.Data.SqlDbType.VarChar);
            query.Parameters["@title"].SqlValue = this.Title;

            query.Parameters.Add("@author", System.Data.SqlDbType.VarChar);
            query.Parameters["@author"].SqlValue = this.Author;

            query.Parameters.Add("@publisher", System.Data.SqlDbType.VarChar);
            query.Parameters["@publisher"].SqlValue = this.Publisher;

            query.Parameters.Add("@lang", System.Data.SqlDbType.VarChar);
            query.Parameters["@lang"].SqlValue = this.Language;

            query.Parameters.Add("@isbn", System.Data.SqlDbType.VarChar);
            query.Parameters["@isbn"].SqlValue = this.ISBN;

            query.Parameters.Add("@description", System.Data.SqlDbType.Text);
            query.Parameters["@description"].SqlValue = this.Description;

            query.Parameters.Add("@num_pages", System.Data.SqlDbType.Int);
            query.Parameters["@num_pages"].SqlValue = this.NumPages;

            query.Parameters.Add("@image_url", System.Data.SqlDbType.VarChar);
            query.Parameters["@image_url"].SqlValue = this.ImageUrl;

            query.ExecuteNonQuery();
        }

        /// <summary>
        /// Finds a book with a given Guid
        /// </summary>
        /// <param name="bookid">guid to search for</param>
        /// <returns>the requestedbook object</returns>
        public static Book GetBook(Guid bookid)
        {
            //searches list of books for guid
            foreach (Book b in books)
                if (b.ID.Equals(bookid))
                    return b;
            

            //create defaults for variables
            String title = "", author = "", publisher = "", language = "", isbn = "", description = "", imageUrl = "";
            int numPages = 0;

            //grab connection to dbms
            SqlConnection cnx = DatabaseConnectionManager.Current.Connection;
            //create the query
            SqlCommand query = new SqlCommand("EXEC get_book @bookid", cnx);
            query.Parameters.Add("@bookid", System.Data.SqlDbType.UniqueIdentifier);
            query.Parameters["@bookid"].SqlValue = bookid;

            //execute the query
            SqlDataReader result = query.ExecuteReader();
            try
            {
                //set info
                if (result.Read())
                {
                    //booki = result.GetGuid(result.GetOrdinal("BookId"));
                    title = result["title"].ToString();
                    author = result["author"].ToString();
                    publisher = result["publisher"].ToString();
                    language = result["lang"].ToString();
                    isbn = result["isbn"].ToString();
                    description = result["description"].ToString();
                    numPages = result.GetInt32(result.GetOrdinal("num_pages"));
                    imageUrl = result["image_url"].ToString();

                }
                //else - the book is INVALID
                else
                {
                    throw new NoSuchBook("The Book does not exist!");
                }
            }
            finally
            {
                result.Close();
            }

            //if we are here... return the book hold object
            Book requestedBook = new Book(bookid, title, author, publisher, language, isbn, description, numPages, imageUrl);
            books.Add(requestedBook);
            return requestedBook;
        }

        /// <summary>
        /// Finds all books by a given author  
        /// </summary>
        /// <param name="author"> author to search for </param>
        /// <returns>list of books found by author</returns>
        public static List<Book> GetBooksByAuthor(String author)
        {
            List<Book> foundBooks = new List<Book>();
            //grab connection to dbms
            SqlConnection cnx = DatabaseConnectionManager.Current.Connection;
            //create query
            SqlCommand query = new SqlCommand("EXEC get_books_by_author @author", cnx);
            query.Parameters.Add("@author", System.Data.SqlDbType.VarChar);
            query.Parameters["@author"].SqlValue = author;
            //execute query
            SqlDataReader result = query.ExecuteReader();
            try
            {
                while (result.Read())
                {
                    foundBooks.Add(Book.GetBook(result.GetGuid(result.GetOrdinal("bookid"))));
                }
            }
            finally
            {
                result.Close();
            }
            return foundBooks;
        }

        /// <summary>
        /// Finds all books by a given title  
        /// </summary>
        /// <param name="title"> title to search for </param>
        /// <returns>list of books found by title</returns>
        public static List<Book> GetBooksByTitle(String title)
        {
            List<Book> foundBooks = new List<Book>();
            //connect to dbms
            SqlConnection cnx = DatabaseConnectionManager.Current.Connection;
            //create the query
            SqlCommand query = new SqlCommand("EXEC get_books_by_title @title", cnx);
            query.Parameters.Add("@title", System.Data.SqlDbType.VarChar);
            query.Parameters["@title"].SqlValue = title;
            //execute query
            SqlDataReader result = query.ExecuteReader();
            try
            {
                while (result.Read())
                {
                    foundBooks.Add(Book.GetBook(result.GetGuid(result.GetOrdinal("bookid"))));
                }
            }
            finally
            {
                result.Close();
            }
            return foundBooks;
        }

        /// <summary>
        /// Finds all books by a given ISBN  
        /// </summary>
        /// <param name="isbn"> ISBN to search for </param>
        /// <returns>list of books found by ISBN</returns>
        public static List<Book> GetBooksByISBN(String isbn)
        {
            List<Book> foundBooks = new List<Book>();
            //establish connection to dbms
            SqlConnection cnx = DatabaseConnectionManager.Current.Connection;
            //create query
            SqlCommand query = new SqlCommand("EXEC get_books_by_isbn @isbn", cnx);
            query.Parameters.Add("@isbn", System.Data.SqlDbType.VarChar);
            query.Parameters["@isbn"].SqlValue = isbn;
            //execute query
            SqlDataReader result = query.ExecuteReader();
            try
            {
                while (result.Read())
                {
                    foundBooks.Add(Book.GetBook(result.GetGuid(result.GetOrdinal("bookid"))));
                }
            }
            finally
            {
                result.Close();
            }
            return foundBooks;
        }

        /// <summary>
        /// Finds all books by a given title and author  
        /// </summary>
        /// <param name="title"> title to search for </param>
        /// <param name="author "> author to search for </param>
        /// <returns>list of books found by title and by author</returns>
        public static List<Book> GetBooksByTitleAndAuthor(String title, String author)
        {
            List<Book> foundBooks = new List<Book>();
            //connect to dbms
            SqlConnection cnx = DatabaseConnectionManager.Current.Connection;
            //create query
            SqlCommand query = new SqlCommand("EXEC get_books_by_title_and_author @title, @author", cnx);
            query.Parameters.Add("@title", System.Data.SqlDbType.VarChar);
            query.Parameters["@title"].SqlValue = title; 
            query.Parameters.Add("@author", System.Data.SqlDbType.VarChar);
            query.Parameters["@author"].SqlValue = author;
            //execute query
            SqlDataReader result = query.ExecuteReader();
            try
            {
                while (result.Read())
                {
                    foundBooks.Add(Book.GetBook(result.GetGuid(result.GetOrdinal("bookid"))));
                }
            }
            finally
            {
                result.Close();
            }
            return foundBooks;
        }

        /// <summary>
        /// Finds all books by a given title or author  
        /// </summary>
        /// <param name="title"> title to search for </param>
        /// <param name="author "> author to search for </param>
        /// <returns>list of books found by title or by author</returns>
        public static List<Book> GetBooksByTitleOrAuthor(String title, String author)
        {
            List<Book> foundBooks = new List<Book>();
            foundBooks.AddRange(GetBooksByTitle(title));
            foundBooks.AddRange(GetBooksByAuthor(author));
            return foundBooks;
        }

    }
}
